<?php
declare(strict_types=1);
namespace Framework\Pool;
use Framework\Libs\CustomException;
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;

class MySql
{
    protected $pools;

    protected $config = [
        'database_name' => 'test',
        'server' => '127.0.0.1',
        'port' => 3306,
        'username' => 'root',
        'password' => 'root',
        'charset' => 'utf8mb4',
        'unixSocket' => null,
        'options'=> [
            //PDO::ATTR_PERSISTENT => true,//连接池使用短链接
            \PDO::ATTR_STRINGIFY_FETCHES => false,
            \PDO::ATTR_CASE => \PDO::CASE_NATURAL,
            //PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>false,
            \PDO::ATTR_ORACLE_NULLS => \PDO::NULL_NATURAL,
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,

            //PDO::ATTR_EMULATE_PREPARES => FALSE,
        ],
        'size' => 16,
    ];

    private static $instance;

    private function __construct(array $config)
    {
        if (empty($this->pools)) {

            $this->config = array_replace_recursive($this->config, $config);
            $this->pools = new PDOPool(
                (new PDOConfig())
                    ->withHost($this->config['server'])
                    ->withPort($this->config['port'])
                    ->withUnixSocket($this->config['unixSocket'])
                    ->withDbName($this->config['database_name'])
                    ->withCharset($this->config['charset'])
                    ->withUsername($this->config['username'])
                    ->withPassword($this->config['password'])
                    ->withOptions($this->config['options']),
                $this->config['size']
            );
        }
    }

    /**
     * @throws \Exception
     */
    public static function getInstance($config = null, $poolName = 'default'): mysql
    {
        if (empty(self::$instance[$poolName])) {
            if (empty($config)) {
                throw new CustomException('redis Config empty');
            }
            if (empty($config['size'])) {
                throw new CustomException('the size of database connection pools cannot be empty');
            }
            self::$instance[$poolName] = new static($config);
        }

        return self::$instance[$poolName];
    }

    /**
     * @param $num
     * @return PDO|\Swoole\Database\PDOProxy|void
     */
    public function getConnection($num=0): \Swoole\Database\PDOProxy
    {
        $num+=1;
        try {
            return $this->pools->get();
        }catch (\PDOException $err){
            xn_log($err->getMessage(),"getConnection");
            if($num<50){
                usleep(1);
                return $this->getConnection($num);
            }else{
                xn_log("db times out","getConnection");
            }
        }catch (\Throwable $err){
            xn_log($err->getMessage(),"getConnection");
            if($num<50){
                usleep(1);
                return $this->getConnection($num);
            }else{
                xn_log("db times out","getConnection");
            }
        }
    }

    public function close($connection = null):void
    {
        try {
            $this->pools->put($connection);
        }catch (\PDOException $err){
            xn_log($err->getMessage(),"Pool");
        }catch (\Throwable $err){
            xn_log($err->getMessage(),"Pool");
        }
    }

    public function getConfig(): array
    {
        return $this->config;
    }

    public function fill(): void
    {
        try {
            $this->pools->fill();
        }catch (\PDOException $err){
            xn_log($err->getMessage(),"fill");
        }catch (\Throwable $err){
            xn_log($err->getMessage(),"fill");
        }
    }
}

?>